package com.tpsix.security;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.*;

@Component
@AllArgsConstructor
public class JwtTokenManager {

    private static final String USER_ID_KEY = "userId";

    private static final String CREATED_KEY = "created";

    private static final String VALIDITY_KEY = "validity";

    private static final String USERNAME_KEY = Claims.SUBJECT;

    private static final String AUTHORITIES_KEY = "authorities";

    private final ProjectConfigurationProperties projectProperties;

    /**
     * Create token
     *
     * @param authentication auth info
     * @return token
     */
    public String createToken(Authentication authentication) {
        return createToken(authentication.getPrincipal());
    }

    @SneakyThrows
    public String createToken(Object principal) {

        // create token
        SecurityUserDetails userDetails = (SecurityUserDetails) principal;

        long now = (new Date()).getTime();

        Date validity = new Date(now + projectProperties.getJwt().getTokenValidityInSeconds() * 1000L);

        Map<String, Object> claims = new HashMap<>();
        claims.put(USERNAME_KEY, userDetails.getUsername());
        claims.put(USER_ID_KEY, userDetails.getUserId());
        claims.put(AUTHORITIES_KEY, JSON.toJSONString(userDetails.getAuthorities()));
        claims.put(CREATED_KEY, now);
        claims.put(VALIDITY_KEY, validity.getTime());

         return Jwts.builder()
                .setClaims(claims)
                .setExpiration(validity)
                .signWith(SignatureAlgorithm.HS256, projectProperties.getJwt().getSecret())
                .compact();
    }

    /**
     * Get auth Info
     *
     * @param token token
     * @return auth info
     */
    public Authentication getAuthentication(String token) {
        // parse the payload of token
        Claims claims = Jwts.parser()
                .setSigningKey(projectProperties.getJwt().getSecret())
                .parseClaimsJws(token)
                .getBody();

        String username = (String) claims.get(USERNAME_KEY);
        int userId = Integer.parseInt(claims.get(USER_ID_KEY).toString());
        Collection<? extends GrantedAuthority> authorities = JSONObject.parseArray((String) claims.get(AUTHORITIES_KEY), SimpleGrantedAuthority.class);

        SecurityUserDetails userDetails = SecurityUserDetails.builder()
                .authorities(authorities)
                .userId(userId)
                .locked(false)
                .username(username)
                .build();

        return new UsernamePasswordAuthenticationToken(userDetails, null, authorities);
    }

    /**
     * validate token
     *
     * @param token token
     * @return whether valid
     */
    public void validateToken(String token) {
        Jwts.parser().setSigningKey(projectProperties.getJwt().getSecret()).parseClaimsJws(token);
    }

}
